package com.jackstraw.jack_base.util;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanDefinitionCustomizer;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;

import java.beans.Introspector;
import java.util.Map;
import java.util.function.Supplier;

/**
 * Spring ApplicationContext 工具类
 */
@Component
public class SpringUtil implements ApplicationContextAware, BeanFactoryAware {

    public static DefaultListableBeanFactory listableBeanFactory;

    private static ApplicationContext applicationContext;

    private static GenericApplicationContext genericApplicationContext;


    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        listableBeanFactory = (DefaultListableBeanFactory) beanFactory;
    }

    public static <T> void registerSingleton(Class<T> type) {
        T obj = listableBeanFactory.createBean(type);
        String beanName = StringUtil.beanName(type.getName());
        listableBeanFactory.registerSingleton(beanName, obj);
    }

    public static void destroy(String className) {
        String beanName = StringUtil.beanName(className);
        listableBeanFactory.destroySingleton(beanName);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        applicationContext = applicationContext;
        genericApplicationContext = (GenericApplicationContext) applicationContext;
        Environment environment = getBean(StandardEnvironment.class);
        EnvironmentUtil.setEnvironmentJack(environment);
    }

    public static void setApplicationContextJack(ApplicationContext applicationContext) {
        SpringUtil.applicationContext = applicationContext;
        SpringUtil.genericApplicationContext = (GenericApplicationContext) applicationContext;
        Environment environment = getBean(StandardEnvironment.class);
        EnvironmentUtil.setEnvironmentJack(environment);
    }

    public static <T> T getBean(String beanName) {
        if (applicationContext.containsBean(beanName)) {
            return (T) applicationContext.getBean(beanName);
        } else {
            return null;
        }
    }

    /**
     * 通过class获取Bean
     *
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getBean(Class<T> clazz) {
        return applicationContext.getBean(clazz);
    }

    /**
     * 获取默认的bean名称
     *
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> String getDefaultBeanName(Class<T> clazz) {
        String shortClassName = ClassUtils.getShortName(clazz);
        return Introspector.decapitalize(shortClassName);
    }

    /**
     * 获取默认的bean名称
     *
     * @param prefix
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> String getDefaultBeanName(String prefix, Class<T> clazz) {
        return prefix.concat(getDefaultBeanName(clazz));
    }

    /**
     * 通过name,以及Clazz返回指定的Bean
     *
     * @param name
     * @param clazz
     * @param <T>
     * @return
     */
    public static <T> T getBean(String name, Class<T> clazz) {
        return getApplicationContext().getBean(name, clazz);
    }

    public static <T> Map<String, T> getBeansOfType(Class<T> baseType) {
        return applicationContext.getBeansOfType(baseType);
    }

    public static <T> void registerBean(@Nullable String beanName, Class<T> beanClass,
                                        @Nullable Supplier<T> supplier, BeanDefinitionCustomizer... customizers) {
        genericApplicationContext.registerBean(beanName, beanClass, supplier, customizers);
    }

    /**
     * 获取applicationContext
     *
     * @return
     */
    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    public static boolean containsBean(String name) {
        return applicationContext.containsBean(name);
    }

    public static boolean isSingleton(String name) {
        return applicationContext.isSingleton(name);
    }

    public static Class<? extends Object> getType(String name) {
        return applicationContext.getType(name);
    }

    @Component
    public static class SpringUtilByHutool extends cn.hutool.extra.spring.SpringUtil {
    }

}